/**
 * \file IfxCpu.h
 * \brief CPU  basic functionality
 * \ingroup IfxLld_Cpu
 *
 * \version iLLD_1_0_1_11_0
 * \copyright Copyright (c) 2019 Infineon Technologies AG. All rights reserved.
 *
 *
 *                                 IMPORTANT NOTICE
 *
 *
 * Use of this file is subject to the terms of use agreed between (i) you or 
 * the company in which ordinary course of business you are acting and (ii) 
 * Infineon Technologies AG or its licensees. If and as long as no such 
 * terms of use are agreed, use of this file is subject to following:


 * Boost Software License - Version 1.0 - August 17th, 2003

 * Permission is hereby granted, free of charge, to any person or 
 * organization obtaining a copy of the software and accompanying 
 * documentation covered by this license (the "Software") to use, reproduce,
 * display, distribute, execute, and transmit the Software, and to prepare
 * derivative works of the Software, and to permit third-parties to whom the 
 * Software is furnished to do so, all subject to the following:

 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer, must
 * be included in all copies of the Software, in whole or in part, and all
 * derivative works of the Software, unless such copies or derivative works are
 * solely in the form of machine-executable object code generated by a source
 * language processor.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.

 *
 *
 * \defgroup IfxLld_Cpu_Std_Core Cpu Core Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Interrupt Interrupt Utility Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Cache Cache Management Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_PerformanceCounter Performance Counter Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Synchronization Synchronization Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Utility Cpu Utility Functions
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_Enum Enumerations
 * \ingroup IfxLld_Cpu_Std
 * \defgroup IfxLld_Cpu_Std_DataStructures Data Structures
 * \ingroup IfxLld_Cpu_Std
 */

#ifndef IFXCPU_H
#define IFXCPU_H 1

/******************************************************************************/
/*----------------------------------Includes----------------------------------*/
/******************************************************************************/

#include "_Impl/IfxCpu_cfg.h"
#include "IfxSrc_reg.h"
#include "IfxScu_reg.h"
#include "IfxStm_reg.h"
#include "_Impl/IfxScu_cfg.h"
#include "_Utilities/Ifx_Assert.h"
#include "Scu/Std/IfxScuWdt.h"
#include "Scu/Std/IfxScuCcu.h"

/******************************************************************************/
/*-----------------------------------Macros-----------------------------------*/
/******************************************************************************/

/** \brief Convert local DSPR address to global DSPR address which can be accessed from the SRI bus.
 * Use this macro to convert a local DSPR address (in segment 0xd00.....) to
 * a global DSPR address (in segment 0x700....., 0x600....., 0x500..... downwards) depending on
 * the CPU number.
 * Example usage:
 *  \code
 *     dmaChConfig.sourceAddress      = IFXCPU_GLB_ADDR_DSPR(IfxCpu_getCoreId(), &sourceBuffer[i][0]);
 *     dmaChConfig.destinationAddress = IFXCPU_GLB_ADDR_DSPR(IfxCpu_getCoreId(), &destinationBuffer[i][0]);
 *  \endcode
 */
#define IFXCPU_GLB_ADDR_DSPR(cpu, address) ((((((unsigned)(address) & 0xF0000000) == 0xD0000000) ? ((((unsigned)(address) & 0x000fffff) | 0x70000000) - ((cpu) * 0x10000000)) : (unsigned)(address))))

/** \brief Convert local PSPR address to global PSPR address which can be accessed from the SRI bus.
 * Use this macro to convert a local PSPR address (in segment 0xc......) to
 * a global PSPR address (in segment 0x701....., 0x601....., 0x501..... downwards) depending on
 * the CPU number.
 *
 *   Example usage:
 *   \code
 *     dmaChConfig.sourceAddress      = IFXCPU_GLB_ADDR_PSPR(IfxCpu_getCoreId(), &sourceBufferInPsprMemory);
 *     dmaChConfig.destinationAddress = IFXCPU_GLB_ADDR_PSPR(IfxCpu_getCoreId(), &destinationBufferInPsprMemory);
 *   \endcode
 */
#define IFXCPU_GLB_ADDR_PSPR(cpu, address) ((((unsigned)(address) & 0x000fffff) | 0x70100000) - ((cpu) * 0x10000000))

/******************************************************************************/
/*------------------------------Type Definitions------------------------------*/
/******************************************************************************/

/** \brief Lock type Spin lock
 */
typedef unsigned int IfxCpu_spinLock;

/** \brief Lock type Mutex lock
 */
typedef unsigned int IfxCpu_mutexLock;

/** \brief Event used for synchronisation.
 */
typedef unsigned int IfxCpu_syncEvent;

/******************************************************************************/
/*--------------------------------Enumerations--------------------------------*/
/******************************************************************************/

/** \addtogroup IfxLld_Cpu_Std_Enum
 * \{ */
/** \brief Enumeration for the Cpu mode
 */
typedef enum
{
    IfxCpu_CoreMode_halt,
    IfxCpu_CoreMode_run,
    IfxCpu_CoreMode_idle,
    IfxCpu_CoreMode_sleep,
    IfxCpu_CoreMode_stby,
    IfxCpu_CoreMode_unknown
} IfxCpu_CoreMode;

/** \brief Performance conunter modes
 */
typedef enum
{
    IfxCpu_CounterMode_normal = 0,  /**< \brief Normal counter mode:the counter increments on their respective triggers */
    IfxCpu_CounterMode_task   = 1   /**< \brief Normal counter mode:additional gating control from the debug unit which allows the data gathered in the performance counters to be filtered by some specific criteria */
} IfxCpu_CounterMode;

/** \brief Overlay Address Mask: determines the overlay block size and the bits used for address comparison and translation
 */
typedef enum
{
    IfxCpu_OverlayAddressMask_32byte  = 0xFFF,  /**< \brief 32 byte block size Mask */
    IfxCpu_OverlayAddressMask_64byte  = 0xFFE,  /**< \brief 64 byte block size Mask */
    IfxCpu_OverlayAddressMask_128byte = 0xFFC,  /**< \brief 128 byte block size Mask */
    IfxCpu_OverlayAddressMask_256byte = 0xFF8,  /**< \brief 256 byte block size Mask */
    IfxCpu_OverlayAddressMask_512byte = 0xFF0,  /**< \brief 512 byte block size Mask */
    IfxCpu_OverlayAddressMask_1KB     = 0xFE0,  /**< \brief 1K byte block size Mask */
    IfxCpu_OverlayAddressMask_2KB     = 0xFC0,  /**< \brief 2K byte block size Mask */
    IfxCpu_OverlayAddressMask_4KB     = 0xF80,  /**< \brief 4K byte block size Mask */
    IfxCpu_OverlayAddressMask_8KB     = 0xF00,  /**< \brief 8K byte block size Mask */
    IfxCpu_OverlayAddressMask_16KB    = 0xE00,  /**< \brief 16K byte block size Mask */
    IfxCpu_OverlayAddressMask_32KB    = 0xC00,  /**< \brief 32K byte block size Mask */
    IfxCpu_OverlayAddressMask_64KB    = 0x800,  /**< \brief 64K byte block size Mask */
    IfxCpu_OverlayAddressMask_128KB   = 0x0     /**< \brief 128K byte block size Mask */
} IfxCpu_OverlayAddressMask;

/** \brief Selects overlay memory used for redirection
 */
typedef enum
{
    IfxCpu_OverlayMemorySelect_core0DsprPspr = 0,  /**< \brief Redirection to Core 0 DSPR/PSPR memory */
    IfxCpu_OverlayMemorySelect_core1DsprPspr = 1   /**< \brief Redirection to Core 1 DSPR/PSPR memory */
} IfxCpu_OverlayMemorySelect;

typedef enum
{
    IfxCpu_ResetStatus_notCpuReset   = 0,  /**< \brief No Kernel Reset was executed */
    IfxCpu_ResetStatus_cpuResetBySmu = 1,  /**< \brief Kernel reset was requested by hardware since last clear (SMU) */
    IfxCpu_ResetStatus_cpuResetBySw  = 2   /**< \brief Kernel reset was requested by software since last clear (by writing KRST0.RST=1 and KRST1.RST=1) */
} IfxCpu_ResetStatus;

/** \} */

/******************************************************************************/
/*-----------------------------Data Structures--------------------------------*/
/******************************************************************************/

/** \addtogroup IfxLld_Cpu_Std_DataStructures
 * \{ */
/** \brief counter
 */
typedef struct
{
    uint32  counter;        /**< \brief Counter value */
    boolean overlfow;       /**< \brief sticky overlfow */
} IfxCpu_Counter;

/** \} */

/** \addtogroup IfxLld_Cpu_Std_DataStructures
 * \{ */
/** \brief Performance counter result
 */
typedef struct
{
    IfxCpu_Counter instruction;       /**< \brief Instruction counter */
    IfxCpu_Counter clock;             /**< \brief CPU clock counter */
    IfxCpu_Counter counter1;          /**< \brief Multi counter 1 */
    IfxCpu_Counter counter2;          /**< \brief Multi counter 2 */
    IfxCpu_Counter counter3;          /**< \brief Multi counter 3 */
} IfxCpu_Perf;

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Core
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to get the address for CPU HW module register memory map
 * \param cpu Resource index of the CPU
 * \return CPU module register address
 */
IFX_INLINE Ifx_CPU *IfxCpu_getAddress(IfxCpu_ResourceCpu cpu);

/** \brief API to get core id of the CPU of the caller.
 * Caution: Core id of the cpu's may not be continguous and shouldn't be used to index cpu.
 * Use IfxCpu_getCoreIndex() to get cpu no.
 * \return Resource index of the CPU.
 */
IFX_INLINE IfxCpu_Id IfxCpu_getCoreId(void);

/** \brief API to get cpu index of the caller CPU.
 * Note: This api can be used whereever cpu no/index is needed.
 * \return Resource index of the CPU.
 */
IFX_INLINE IfxCpu_ResourceCpu IfxCpu_getCoreIndex(void);

/** \brief API to initialize the context save area of the CPU where this is called.
 *
 * This API can initialize the CSA of the host CPU where this API is called. This API
 * shall not be used to initialize the CSA of another CPU
 * \param csaBegin Pointer to start of context save area
 * \param csaEnd Pointer to end of context save area
 * \return None
 */
IFX_INLINE void IfxCpu_initCSA(uint32 *csaBegin, uint32 *csaEnd);

/** \brief Set/Clear safety task identifier (PSW.S) on current CPU
 * \return None
 */
IFX_INLINE void IfxCpu_setSafetyTaskIdentifier(boolean safetyId);

/** \brief Triggers Software Reset
 * \return None
 */
IFX_INLINE void IfxCpu_triggerSwReset(void);

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to get current mode of CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \return Current mode of the CPU
 */
IFX_EXTERN IfxCpu_CoreMode IfxCpu_getCoreMode(Ifx_CPU *cpu);

/** \brief API to get current mode of CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \return Resource index of the CPU
 */
IFX_EXTERN IfxCpu_ResourceCpu IfxCpu_getIndex(Ifx_CPU *cpu);

/** \brief API to set mode of the CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \param mode CPU mode to be set by this API
 * \return Success status of the activity (setting the core mode).
 * \retval TRUE: If the activity successfully be performed.
 * \retval FALSE: If the activity can't be performed.
 */
IFX_EXTERN boolean IfxCpu_setCoreMode(Ifx_CPU *cpu, IfxCpu_CoreMode mode);

/** \brief API to set the program counter for the CPU specified.
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \param programCounter Program counter value to be set
 * \return success status of the activity (setting program counter value).
 * \retval TRUE: If the activity successfully be performed.
 * \retval FALSE: If the activity can't be performed
 */
IFX_EXTERN boolean IfxCpu_setProgramCounter(Ifx_CPU *cpu, uint32 programCounter);

/** \brief API to set the program counter for the CPU specified and start the CPU
 * \param cpu Pointer to the CPU HW module (register memory map)
 * \param programCounter Program counter value to start the CPU
 * \return success status of the activity (setting program counter value).
 * \retval TRUE: If the activity successfully be performed.
 * \retval FALSE: If the activity can't be performed
 */
IFX_EXTERN boolean IfxCpu_startCore(Ifx_CPU *cpu, uint32 programCounter);

/** \brief To request reset of a particular core, given by coreId
 * \return None
 */
IFX_EXTERN void IfxCpu_triggerCpuReset(IfxCpu_ResourceCpu coreIndex);

/** \brief Retrieve the cause of reset(no reset, hardware(SMU) or software) since last clear by reading KRST0.RSTSTAT and clear it.
 */
IFX_EXTERN IfxCpu_ResetStatus IfxCpu_getCpuResetStatus(IfxCpu_ResourceCpu coreIndex);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Interrupt
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to get the status of global interrupt enable (ICR.IE) for the CPU which calls this API
 * This API provides the status of CPU where this API is called
 * \return Status of global interrupt enable bit.
 * \retval TRUE: Global interrupts enabled.
 * \retval FALSE: Global interrupts disabled
 */
IFX_INLINE boolean IfxCpu_areInterruptsEnabled(void);

/** \brief API to disable global interrupt and return the previous status.
 *
 * This API can be used only to disable the global interrupts of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \return Previous status of global interrupt enable bit.
 * \retval TRUE: Previously, global interrupts enabled.
 * \retval FALSE: Previously, global interrupts disabled
 */
IFX_INLINE boolean IfxCpu_disableInterrupts(void);

/** \brief API to enable global interrupt.
 * This API simply enables the global interrupt.
 * \return None
 */
IFX_INLINE void IfxCpu_enableInterrupts(void);

/** \brief Disable the Global Interrupt
 * \return None
 */
IFX_INLINE void IfxCpu_forceDisableInterrupts(void);

/** \brief API to restore global interrupt with that of the passed parameter.
 *
 * This API can be used only to disable the global interrupts of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \param enabled Previous status of the global interrupt enable bit
 * \return None
 */
IFX_INLINE void IfxCpu_restoreInterrupts(boolean enabled);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Cache
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to enable/ disable the data cacheability for selected segments
 * With this API cacheability for one or more segment can be enabled/disabled for the CPU core where this API is called.
 * \Note This API is to be called only if the PCACHE or DCACHE are not enabled before
 * \param segmentNumberMask Mask where bitfield 0 represents segment 0 and bitfield 16 represent segment F.
 * \param enable TRUE: to enable the cacheability for selected segment, FALSE: to disable.
 * \return None
 */
IFX_INLINE void IfxCpu_enableSegmentSpecificDataAccessCacheability(uint16 segmentNumberMask, boolean enable);

/** \brief API to enable/ disable the instruction cacheability for selected segments
 * With this API cacheability for one or more segment can be enabled/disabled for the CPU core where this API is called.
 * \Note This API is to be called only if the PCACHE or DCACHE are not enabled before
 * \param segmentNumberMask Mask where bitfield 0 represents segment 0 and bitfield 16 represent segment F.
 * \param enable TRUE: to enable the cacheability for selected segment, FALSE: to disable.
 * \return None
 */
IFX_INLINE void IfxCpu_enableSegmentSpecificInstructionAccessCacheability(uint16 segmentNumberMask, boolean enable);

/** \brief API to invalidate the program cache
 * \return None
 */
IFX_INLINE void IfxCpu_invalidateProgramCache(void);

/** \brief API to determine if an address is in a cachable or non-cachable Flash/LMU section
 * \param address Address
 * \return Status TRUE/FALSE
 */
IFX_INLINE boolean IfxCpu_isAddressCachable(void *address);

/** \brief API to enable or bypass the data cache for the CPU which calls this API.
 *
 * This API can be used only to enable or bypass the data cache of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \param enable Command to enable or bypass the data cache
 * TRUE: Enable the data cache.
 * FALSE: Bypass the data cache.
 * \return None
 */
IFX_INLINE void IfxCpu_setDataCache(boolean enable);

/** \brief API to enable or bypass the program cache for the CPU which calls this API.
 *
 * This API can be used only to enable or bypass the program cache of caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \param enable Command to enable or bypass the program cache.
 * TRUE: Enable the program cache.
 * FALSE: Bypass the program cache
 * \return None
 */
IFX_INLINE void IfxCpu_setProgramCache(boolean enable);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_PerformanceCounter
 * \{ */

/******************************************************************************/
/*-------------------------Inline Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to read the clock counter for the CPU which calls this API.
 *
 * This API can be used to read clock counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \return Counter value. 0 to 0x7FFFFFFF.
 */
IFX_INLINE uint32 IfxCpu_getClockCounter(void);

/** \brief API to get sticky overflow bit of clock counter for the CPU, which calls this API.
 *
 * This API can be used to get sticky overflow bit of clock counter of only the caller CPU.
 * It cannot be used for this activity towards other CPUs.
 * This API also clears the sticky overflow after the read. While reading the sticky bit this API disables
 * the counter for short time. (otherwise sticky bit cannot be cleared). This API shall be used after
 * reading the counter
 * \return Status of sticky overflow bit.
 * \retval TRUE: Sticky overflow bit is set.
 * \retval FALSE: Sticky overflow bit is reset
 */
IFX_INLINE boolean IfxCpu_getClockCounterStickyOverflow(void);

/** \brief API to read the instruction counter for the CPU which calls this API.
 *
 * This API can be used to read instruction counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs
 * \return Counter value. 0 to 0x7FFFFFFF.
 */
IFX_INLINE uint32 IfxCpu_getInstructionCounter(void);

/** \brief API to get sticky overflow bit of Instruction counter for the CPU, which calls this API.
 *
 * This API can be used to get sticky overflow bit of Instruction counter of only the caller CPU.
 * It cannot be used for this activity towards other CPUs.
 * This API also clears the sticky overflow after the read. While reading the sticky bit this API disables
 * the counter for short time. (otherwise sticky bit cannot be cleared). This API shall be used after
 * reading the counter
 * \return Status of sticky overflow bit.
 * \retval TRUE: Sticky overflow bit is set.
 * \retval FALSE: Sticky overflow bit is reset
 */
IFX_INLINE boolean IfxCpu_getInstructionCounterStickyOverflow(void);

/** \brief API to read the performance counter for the CPU which calls this API.
 * \param address Address
 * \return counter value
 */
IFX_INLINE uint32 IfxCpu_getPerformanceCounter(uint16 address);

/** \brief API to get sticky overflow bit of performance counter for the CPU, which calls this API.
 * This is generic function to get sticky overflow bit of any performance counters
 * \param address Address
 * \return Status
 */
IFX_INLINE boolean IfxCpu_getPerformanceCounterStickyOverflow(uint16 address);

/** \brief Reset and start instruction, clock and multi counters
 *
 * Reset and start CCNT, ICNT, M1CNT, M2CNT, M3CNT. the overflow bits are cleared.
 * \param mode Counter mode
 * \return None
 */
IFX_INLINE void IfxCpu_resetAndStartCounters(IfxCpu_CounterMode mode);

/** \brief API to enable or disable performance counter for the CPU which calls this API.
 *
 * This API can be used to enable or disable performance counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \param enable enable Command to enable or disable the performance counter.
 * TRUE: Enable the performance counter.
 * FALSE: Disable the performance counter
 * \return None
 */
IFX_INLINE void IfxCpu_setPerformanceCountersEnableBit(uint32 enable);

#if !((defined(__cplusplus)) && (defined(__TASKING__)))
/** \brief Stop instruction and clock counters, return their values
 *
 * Stop CCNT, ICNT, M1CNT, M2CNT, M3CNT and return their values;
 *  \Note The CCTRL is reset to 0, for more accurate measurements and has to be initialized again before strating the next performance measurement.
 * \return Performance counter result
 */
IFX_INLINE IfxCpu_Perf IfxCpu_stopCounters(void);
#endif

/** \brief API to update clock counter for the CPU which calls this API.
 *
 * This API can be used to update clock counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \param count Counter value to be updated. 0 to 0x7FFFFFFF
 * \return None
 */
IFX_INLINE void IfxCpu_updateClockCounter(uint32 count);

/** \brief API to update Instruction counter for the CPU which calls this API.
 *
 * This API can be used to update Instruction counter of only the caller CPU. It cannot be
 * used for this activity towards other CPUs.
 * \param count Counter value to be updated. 0 to 0x7FFFFFFF
 * \return None
 */
IFX_INLINE void IfxCpu_updateInstructionCounter(uint32 count);

/** \brief API to update performance counter for the CPU which calls this API.
 * This is generic function to update any of the performance counters
 * \param address Address
 * \param count Count
 * \return None
 */
IFX_INLINE void IfxCpu_updatePerformanceCounter(uint32 address, uint32 count);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Synchronization
 * \{ */

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief API to acquire the mutex (binary semaphore).
 *
 * This API can be used to acquire/get the mutex.
 * \param lock lock pointer
 * \return TRUE : lock acquired successfully. FALSE: Failed to acquire the lock
 *
 * \code
 *    IfxCpu_mutexLock resourceLock;
 *    boolean flag = IfxCpu_acquireMutex(&resourceLock);
 *    if (flag){
 *      // critical section
 *      IfxCpu_releaseMutex(&resourceLock);
 *    }
 * \endcode
 *
 */
IFX_EXTERN boolean IfxCpu_acquireMutex(IfxCpu_mutexLock *lock);

/** \brief API to unlock the mutex .
 *
 * This API can be used to unlock the previously acquired mutex
 * \param lock lock pointer
 * \return None
 *
 * \code
 *    IfxCpu_mutexLock resourceLock;
 *    boolean flag = IfxCpu_acquireMutex(&resourceLock);
 *    if (flag){
 *      // critical section
 *      IfxCpu_releaseMutex(&resourceLock);
 *    }
 * \endcode
 *
 */
IFX_EXTERN void IfxCpu_releaseMutex(IfxCpu_mutexLock *lock);

/** \brief API to unlock the resource .
 *
 * This API can be used to unlock the previously acquired lock
 * \param lock lock pointer
 * \return None
 */
IFX_EXTERN void IfxCpu_resetSpinLock(IfxCpu_spinLock *lock);

/** \brief API to lock the resource in spin mode with the given timeout.
 *
 * This API can be used to spin lock for the lock for the given timeout period.
 * \param lock lock pointer
 * \param timeoutCount loop counter value used for timeout to acquire lock
 * \return TRUE : lock acquired successfully. FALSE: Failed to acquire the lock
 *
 * \code
 *    IfxCpu_spinLock resourceLock;
 *    boolean flag = IfxCpu_setSpinLock(&resourceLock, 0xFFFF);
 *    if (flag){
 *      // critical section
 *      IfxCpu_resetSpinLock(&resourceLock);
 *    }
 * \endcode
 *
 */
IFX_EXTERN boolean IfxCpu_setSpinLock(IfxCpu_spinLock *lock, uint32 timeoutCount);

/** \} */

/** \addtogroup IfxLld_Cpu_Std_Utility
 * \{ */

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief This function disables the overlay memory.
 * \param cpu Core no
 * \param overlayBlock Overlay Block
 * \return None
 */
IFX_EXTERN void IfxCpu_disableOverlayBlock(IfxCpu_ResourceCpu cpu, uint16 overlayBlock);

/** \brief This function configures and enables the overlay memory.
 * Note:
 * The data overlay provides the capability to redirect selected data accesses to the Overlay memory. Data accesses made by the TriCore to Program Flash, Online Data Acquisition space, or EBU space can be redirected. Overlay memory may be located in the Local Memory (if present), in the Emulation Memory (Emulation Device only), or in the EBU space,or in the DPSR/PSPR memory.
 *
 * How the overlay Memory works?
 * Any data access to segment 8 or segment A is checked against all the activated overlay blocks. For each activated overlay block, address bits 27..5 are compared with the target base address , and this bit-wise comparison is qualified by the content of address mask. Address bits participate in the comparison if the corresponding mask bits are set to one. The access is redirected, if all the address bits selected by mask equal to the corresponding bits in target address.
 * The address for redirection is constructed as follows:
 * 1. Address bits 31..22 are set according to the overlay memory selection and the cache-ability of the original address.
 * 2. For address bits 21..5:
 * - If the corresponding address mask bit is set, the address bit value is taken from overlay base address .
 * - If the corresponding address mask bit is cleared, the address bit value is taken from the original address.
 * 3. Address bits 4..0 are always taken directly from the original address.
 * If there is no redirection, the original address is used to perform the access.
 * Target address ranges for activated overlay blocks should not overlap or an exception may occur.
 * \param cpu Core no
 * \param overlayBlock Overlay Block
 * \param overlayMemorySelect Overlay Memory Select
 * \param overlayAddressMask overlay Address Mask
 * \param targetBaseAddress Target Base address (i.e Source address to be re-directed)
 * \param overlayBaseAddress Overlay or Re-directed Base address (Bits 21..5 of the base address the overlay memory block in the overlay memory)
 * \return None
 */
IFX_EXTERN void IfxCpu_enableOverlayBlock(IfxCpu_ResourceCpu cpu, uint16 overlayBlock, IfxCpu_OverlayMemorySelect overlayMemorySelect, IfxCpu_OverlayAddressMask overlayAddressMask, uint32 targetBaseAddress, uint32 overlayBaseAddress);

/** \brief API to get random value
 * \param seed Pointer to seed value
 * \return random value
 */
IFX_EXTERN uint32 IfxCpu_getRandomValue(uint32 *seed);

/** \brief API to get random value with in the range
 * \param seed Pointer to seed value
 * \param min minimum range value
 * \param max maximum range value
 * \return random value
 */
IFX_EXTERN uint32 IfxCpu_getRandomValueWithinRange(uint32 *seed, uint32 min, uint32 max);

/** \brief This function waits till all the cores have set their corresponding bits in the event. This function along with
 * IfxCpu_emitEvent() are used to achieve the synchronisation between the configured cores. By default
 * "IFXCPU_CFG_ALLCORE_DONE" macro defined for all the cores. In case the user wants to check for
 * synchronisation among the required cores, the macro can be redefined with the value accroing to the
 * CORE_ID register.
 * The IfxCpu_emitEvent() is to be used in the Main functions of the Cores where the user wants to check for synchronisation.
 *
 * e.g:
 * 1. Check for synchronisation between core 0 and core 5
 * # define 0x41U
 * 2. Check for synchronisation between core 0 to core 5
 * # define 0x5FU
 *
 * Note:
 * Core id values read from CORE_ID register will be as shown below. The value indicates the position of the bit needs to be set while building the macro.
 * Core 0: 0
 * Core 1: 1
 * Core 2: 2
 * Core 3: 3
 * Core 4: 4
 * Core 5: 6
 * \param event Synchronous Event
 * \param timeoutMilliSec timeout in millisec
 * \return Error condition
 *
 * The functions IfxCpu_waitEvent and IfxCpu_emitEvent are used to achieve synchronisation between all cores (i.e individual cores wait till all cores have reached the synchronisation point). The IfxCpu_waitEvent returns 1 incase a timeout occurs.
 *
 * \code
 * // Global variable. preferably located in  shared memory.
 * IfxCpu_syncEvent event;
 * boolean errorVal;
 *
 * // Below code should be repeated in Each core
 *
 * // Upon reaching Synchonisation point
 * IfxCpu_emitEvent(&event);
 * errorVal = IfxCpu_waitEvent(&event, timeoutMilliSec); // timeoutMilliSec is timeout value to wait
 *
 * \endcode
 *
 */
IFX_EXTERN boolean IfxCpu_waitEvent(IfxCpu_syncEvent *event, uint32 timeoutMilliSec);

/** \brief This function sets a bit corresponding to the core in the event.
 * \param event Synchronous Event
 * \return None
 *
 * A coding example can be found in \ref IfxCpu_waitEvent
 *
 */
IFX_EXTERN void IfxCpu_emitEvent(IfxCpu_syncEvent *event);

/** \} */

/******************************************************************************/
/*-------------------------Global Function Prototypes-------------------------*/
/******************************************************************************/

/** \brief Sets all CPUs to Idle mode except the Master CPU,index of which is given as the parameter
 * \param masterCpu Master CPU Index- All except this will be set to Idle Mode
 * \return None
 */
IFX_EXTERN void IfxCpu_setAllIdleExceptMasterCpu(IfxCpu_ResourceCpu masterCpu);

/** \brief Disables interrupts for all CPUs except the Master CPU,index of which is given as the parameter
 * \param masterCpu Master CPU Index- All except this will have their interrupts disabled
 * \return None
 */
IFX_EXTERN void IfxCpu_disableInterruptsAllExceptMaster(IfxCpu_ResourceCpu masterCpu);

/******************************************************************************/
/*---------------------Inline Function Implementations------------------------*/
/******************************************************************************/

IFX_INLINE boolean IfxCpu_areInterruptsEnabled(void)
{
    Ifx_CPU_ICR reg;
    reg.U = __mfcr(CPU_ICR);
    return reg.B.IE != 0;
}


IFX_INLINE boolean IfxCpu_disableInterrupts(void)
{
    boolean enabled;
    enabled = IfxCpu_areInterruptsEnabled();
    __disable();
    __nop();
    return enabled;
}


IFX_INLINE void IfxCpu_enableInterrupts(void)
{
    __enable();
}


IFX_INLINE void IfxCpu_enableSegmentSpecificDataAccessCacheability(uint16 segmentNumberMask, boolean enable)
{
    uint32 cpu_pmaVal;
    uint16 checkRestrictionMask;
    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);

    /*resolve the restrictions*/
    /*In PMA0 Segment-C and Segment[7-CoreID] must have the same value */
    checkRestrictionMask = ((uint16)1 << (7 - coreIndex)) | ((uint16)1 << 0xC);

    if ((segmentNumberMask & checkRestrictionMask) != 0)
    {
        segmentNumberMask |= checkRestrictionMask;
    }

    cpu_pmaVal = __mfcr(CPU_PMA0);                                                              /* Read the CPU_PMA0 */

    cpu_pmaVal = enable ? (cpu_pmaVal | segmentNumberMask) : (cpu_pmaVal & ~segmentNumberMask); /* enable or disable the corresponding bitfield */

    /*The CPU_PMA registers are ENDINIT protected*/
    IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    /*When changing the value of the CPU_PMAx registers both the instruction and data caches should be invalidated*/
    /*Write to PMA0 register for selecting the cacheability for data cache*/
    __dsync();      /* DSYNC instruction should be executed immediately prior to the MTCR*/
    __mtcr(CPU_PMA0, cpu_pmaVal);
    __isync();      /* ISYNC instruction executed immediately following MTCR */
    IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
}


IFX_INLINE void IfxCpu_enableSegmentSpecificInstructionAccessCacheability(uint16 segmentNumberMask, boolean enable)
{
    uint32 cpu_pmaVal;
    uint16 checkRestrictionMask;
    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);

    /*resolve the restrictions*/
    /*In PMA1 Segment-D and Segment[7-CoreID] must have the same value */
    checkRestrictionMask = ((uint16)1 << (7 - coreIndex)) | ((uint16)1 << 0xD);

    if ((segmentNumberMask & checkRestrictionMask) != 0)
    {
        segmentNumberMask |= checkRestrictionMask;
    }

    cpu_pmaVal = __mfcr(CPU_PMA1);                                                              /* Read the CPU_PMA1 */

    cpu_pmaVal = enable ? (cpu_pmaVal | segmentNumberMask) : (cpu_pmaVal & ~segmentNumberMask); /* enable or disable the corresponding bitfield */

    /*The CPU_PMA registers are ENDINIT protected*/
    IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    /*When changing the value of the CPU_PMAx registers both the instruction and data caches should be invalidated*/
    /*Write to PMA1 register for selecting the cacheability for data cache*/
    __dsync();      /* DSYNC instruction should be executed immediately prior to the MTCR */
    __mtcr(CPU_PMA1, cpu_pmaVal);
    __isync();      /* ISYNC instruction executed immediately following MTCR */
    IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
}


IFX_INLINE void IfxCpu_forceDisableInterrupts(void)
{
    __disable();
    __nop();
}


IFX_INLINE Ifx_CPU *IfxCpu_getAddress(IfxCpu_ResourceCpu cpu)
{
    Ifx_CPU *module;

    if (cpu < IfxCpu_ResourceCpu_none)
    {
        module = (Ifx_CPU *)IfxCpu_cfg_indexMap[cpu].module;
    }
    else
    {
        module = NULL_PTR;
    }

    return module;
}


IFX_INLINE uint32 IfxCpu_getClockCounter(void)
{
    return IfxCpu_getPerformanceCounter(CPU_CCNT);
}


IFX_INLINE boolean IfxCpu_getClockCounterStickyOverflow(void)
{
    return IfxCpu_getPerformanceCounterStickyOverflow(CPU_CCNT);
}


IFX_INLINE IfxCpu_Id IfxCpu_getCoreId(void)
{
    Ifx_CPU_CORE_ID reg;
    reg.U = __mfcr(CPU_CORE_ID);
    return (IfxCpu_Id)reg.B.CORE_ID;
}


IFX_INLINE IfxCpu_ResourceCpu IfxCpu_getCoreIndex(void)
{
    Ifx_CPU_CORE_ID reg;
    reg.U = __mfcr(CPU_CORE_ID);
    return (IfxCpu_ResourceCpu)reg.B.CORE_ID;
}


IFX_INLINE uint32 IfxCpu_getInstructionCounter(void)
{
    return IfxCpu_getPerformanceCounter(CPU_ICNT);
}


IFX_INLINE boolean IfxCpu_getInstructionCounterStickyOverflow(void)
{
    return IfxCpu_getPerformanceCounterStickyOverflow(CPU_ICNT);
}


IFX_INLINE uint32 IfxCpu_getPerformanceCounter(uint16 address)
{
    Ifx_CPU_CCNT ccnt;

    if (address == CPU_CCNT)
    {
        ccnt.U = __mfcr(CPU_CCNT);
    }
    else if (address == CPU_ICNT)
    {
        ccnt.U = __mfcr(CPU_ICNT);
    }
    else if (address == CPU_M1CNT)
    {
        ccnt.U = __mfcr(CPU_M1CNT);
    }
    else if (address == CPU_M2CNT)
    {
        ccnt.U = __mfcr(CPU_M2CNT);
    }
    else if (address == CPU_M3CNT)
    {
        ccnt.U = __mfcr(CPU_M3CNT);
    }

    return ccnt.B.COUNTVALUE;
}


IFX_INLINE boolean IfxCpu_getPerformanceCounterStickyOverflow(uint16 address)
{
    Ifx_CPU_CCNT ccnt;

    if (address == CPU_CCNT)
    {
        ccnt.U = __mfcr(CPU_CCNT);
    }
    else if (address == CPU_ICNT)
    {
        ccnt.U = __mfcr(CPU_ICNT);
    }
    else if (address == CPU_M1CNT)
    {
        ccnt.U = __mfcr(CPU_M1CNT);
    }
    else if (address == CPU_M2CNT)
    {
        ccnt.U = __mfcr(CPU_M2CNT);
    }
    else if (address == CPU_M3CNT)
    {
        ccnt.U = __mfcr(CPU_M3CNT);
    }

    return ccnt.B.SOVF;
}


IFX_INLINE void IfxCpu_initCSA(uint32 *csaBegin, uint32 *csaEnd)
{
    uint32  k;
    uint32  nxt_cxi_val = 0;
    uint32 *prvCsa      = 0U;
    uint32 *nxtCsa      = csaBegin;
    uint32  numOfCsa    = (((uint32)csaEnd - (uint32)csaBegin) / 64U);

    for (k = 0; k < numOfCsa; k++)
    {
        nxt_cxi_val = ((uint32)nxtCsa & (0XFU << 28)) >> 12 | ((uint32)nxtCsa & (0XFFFFU << 6)) >> 6;

        if (k == 0)
        {
            __mtcr(CPU_FCX, nxt_cxi_val);   /* store the new pcxi value to LCX */
        }
        else
        {
            *prvCsa = nxt_cxi_val;  /* Store null pointer in last CSA (= very first time!) */
        }

        if (k == (numOfCsa - 3U))
        {
            __mtcr(CPU_LCX, nxt_cxi_val);   /* Last but 2 context save area is pointed in LCX to know if there is CSA depletion */
        }

        prvCsa  = (uint32 *)nxtCsa;
        nxtCsa += 16;           /* next CSA */
    }

    *prvCsa = 0;
}


IFX_INLINE void IfxCpu_invalidateProgramCache(void)
{
    uint16 cpuWdtPassword = IfxScuWdt_getCpuWatchdogPassword();
    {
        IfxScuWdt_clearCpuEndinit(cpuWdtPassword);
        Ifx_CPU_PCON1 pcon1;
        pcon1.U       = __mfcr(CPU_PCON1);
        pcon1.B.PCINV = 1;
        __mtcr(CPU_PCON1, pcon1.U);
        IfxScuWdt_setCpuEndinit(cpuWdtPassword);
    }
}


IFX_INLINE boolean IfxCpu_isAddressCachable(void *address)
{
    uint8 segment = (uint32)address >> 24;
    return ((segment == IFXCPU_CACHABLE_FLASH_SEGMENT) || (segment == IFXCPU_CACHABLE_LMU_SEGMENT)) ? TRUE : FALSE;
}


IFX_INLINE void IfxCpu_resetAndStartCounters(IfxCpu_CounterMode mode)
{
    Ifx_CPU_CCTRL cctrl;
    cctrl.U    = __mfcr(CPU_CCTRL);
    /*Disable the counters */
    cctrl.B.CE = 0;
    __mtcr(CPU_CCTRL, cctrl.U);

    /* reset the counters */
    __mtcr(CPU_CCNT, 0);
    __mtcr(CPU_ICNT, 0);
    __mtcr(CPU_M1CNT, 0);
    __mtcr(CPU_M2CNT, 0);
    __mtcr(CPU_M3CNT, 0);

    /*Enable the counters, set the counter mode */
    cctrl.B.CE = 1;
    cctrl.B.CM = mode;
    __mtcr(CPU_CCTRL, cctrl.U);
}


IFX_INLINE void IfxCpu_restoreInterrupts(boolean enabled)
{
    if (enabled != FALSE)
    {
        __enable();
    }
}


IFX_INLINE void IfxCpu_setDataCache(boolean enable)
{
    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);
    /*PCACHE enable steps */
    {                           /* Step 1: Set PCBYP to 0 if cache is enabled */
        IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
        Ifx_CPU_DCON0 dcon0;
        dcon0.U       = 0;
        dcon0.B.DCBYP = enable ? 0 : 1; /*depending on the enable bypas bit is reset/set */
        __mtcr(CPU_DCON0, dcon0.U);
        IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    }
    /* Step 2: Call Isync */
    __isync();
}


IFX_INLINE void IfxCpu_setPerformanceCountersEnableBit(uint32 enable)
{
    Ifx_CPU_CCTRL cctrl;
    cctrl.U    = __mfcr(CPU_CCTRL);
    cctrl.B.CE = enable;
    __mtcr(CPU_CCTRL, cctrl.U);
}


IFX_INLINE void IfxCpu_setProgramCache(boolean enable)
{
    if (enable)
    {                           /* Step 3: Initiate invalidation of current cache contents if any */
        Ifx_CPU_PCON1 pcon1;
        pcon1.U       = 0;
        pcon1.B.PCINV = 1;
        __mtcr(CPU_PCON1, pcon1.U);
    }

    uint32 coreIndex   = IfxCpu_getCoreIndex();
    uint16 wdtPassword = IfxScuWdt_getCpuWatchdogPasswordInline(&MODULE_SCU.WDTCPU[coreIndex]);
    /*PCACHE enable steps */
    {                           /* Step 1: Set PCBYP to 0 if cache is enabled */
        IfxScuWdt_clearCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
        Ifx_CPU_PCON0 pcon0;
        pcon0.U       = 0;
        pcon0.B.PCBYP = enable ? 0 : 1; /*depending on the enable bypass bit is reset/set */
        __mtcr(CPU_PCON0, pcon0.U);
        IfxScuWdt_setCpuEndinitInline(&MODULE_SCU.WDTCPU[coreIndex], wdtPassword);
    }
    /* Step 2: Call Isync */
    __isync();
}


IFX_INLINE void IfxCpu_setSafetyTaskIdentifier(boolean safetyId)
{
    Ifx_CPU_PSW psw;
    IFX_ASSERT(IFX_VERBOSE_LEVEL_ERROR, (safetyId == 0 || safetyId == 1));
    psw.U   = __mfcr(CPU_PSW);
    psw.B.S = safetyId;
    __mtcr(CPU_PSW, (uint32)psw.U);
}


#if !((defined(__cplusplus)) && (defined(__TASKING__)))
IFX_INLINE IfxCpu_Perf IfxCpu_stopCounters(void)
{
    IfxCpu_Perf result;
    /*Disable the counters, reset the control reg */
    /* Use inline assembly to ensure constant implementation, and execution of the measurement routines */
    __stopPerfCounters();

    Ifx_CPU_CCNT ccnt;
    ccnt.U                = __mfcr(CPU_CCNT);
    result.clock.counter  = ccnt.B.COUNTVALUE;
    result.clock.overlfow = ccnt.B.SOVF;

    Ifx_CPU_ICNT icnt;
    icnt.U                      = __mfcr(CPU_ICNT);
    result.instruction.counter  = icnt.B.COUNTVALUE;
    result.instruction.overlfow = icnt.B.SOVF;

    Ifx_CPU_M1CNT m1cnt;
    m1cnt.U                  = __mfcr(CPU_M1CNT);
    result.counter1.counter  = m1cnt.B.COUNTVALUE;
    result.counter1.overlfow = m1cnt.B.SOVF;

    Ifx_CPU_M2CNT m2cnt;
    m2cnt.U                  = __mfcr(CPU_M2CNT);
    result.counter2.counter  = m2cnt.B.COUNTVALUE;
    result.counter2.overlfow = m2cnt.B.SOVF;

    Ifx_CPU_M3CNT m3cnt;
    m3cnt.U                  = __mfcr(CPU_M3CNT);
    result.counter3.counter  = m3cnt.B.COUNTVALUE;
    result.counter3.overlfow = m3cnt.B.SOVF;

    return result;
}
#endif


IFX_INLINE void IfxCpu_triggerSwReset(void)
{
    MODULE_SCU.SWRSTCON.B.SWRSTREQ = 1;

    /* Wait till reset */
    while (1)
    {}
}


IFX_INLINE void IfxCpu_updateClockCounter(uint32 count)
{
    IfxCpu_updatePerformanceCounter(CPU_CCNT, count);
}


IFX_INLINE void IfxCpu_updateInstructionCounter(uint32 count)
{
    IfxCpu_updatePerformanceCounter(CPU_ICNT, count);
}


IFX_INLINE void IfxCpu_updatePerformanceCounter(uint32 address, uint32 count)
{
    IFX_UNUSED_PARAMETER(address);
    Ifx_CPU_CCTRL cctrl;
    boolean       enableBit;
    /*Disable the counters */
    cctrl.U    = __mfcr(CPU_CCTRL);
    enableBit  = cctrl.B.CE;
    cctrl.B.CE = 0;
    __mtcr(CPU_CCTRL, cctrl.U);

    /*Update the counter value */
    count &= ~(1U << 31);       /*clear sticky overflow bit if set */
    __mtcr(address, count);

    /*restore the enable bit */
    cctrl.B.CE = enableBit;
    __mtcr(CPU_CCTRL, cctrl.U);
}


#endif /* IFXCPU_H */
